#include "stdafx.h"
#include "TexLineSceneNode.h"
#include "CMeshBuffer.h"
#include "ITexture.h"
#include "S3DVertex.h"
#include "SMesh.h"
#include "IVideoDriver.h"
#include "ISceneManager.h"
#include "IMaterialRenderer.h"


using namespace irr;
using namespace irr::core;
using namespace irr::scene;
using namespace irr::video;

// -------------------------------------------------------------------------------
// Textured line Scene Node implementation
// -------------------------------------------------------------------------------

//! constructor
CTexLineSceneNode::CTexLineSceneNode(u32 maxSegments, s32 id) :
	CMeshSceneNode(NULL, id),
	MaxSegments(maxSegments),
	AlteredVertMap(-1),
	Orientation(0.0f, -1.0f, 0.0f)
{
	// creating mesh buffer
	CMeshBuffer<S3DVertex>* meshBuffer = new CMeshBuffer<S3DVertex>();
	Vertices = &(meshBuffer->Vertices);
	Indices  = &(meshBuffer->Indices);

	// initializing mesh structure
	VerticesCount = 2 + 2 * MaxSegments;
	Vertices->reallocate(VerticesCount);
	Vertices->set_used(VerticesCount);
	setTextureBorders(0.0f, 1.0f);

	Indices->reallocate(6 * MaxSegments); // 6 indices for each segment (polygon)
	Indices->set_used(6 * MaxSegments);
	for (u16 i = 0, j = 0; i < VerticesCount - 2; i += 2)
	{
		(*Indices)[j++] = i;
		(*Indices)[j++] = i + 1;
		(*Indices)[j++] = i + 2;

		(*Indices)[j++] = i + 2;
		(*Indices)[j++] = i + 1;
		(*Indices)[j++] = i + 3;
	}

	SMaterial &mat = meshBuffer->getMaterial();
	mat.setFlag(EMF_LIGHTING, false);
	mat.setMaterialType(EMT_TRANSPARENT_ALPHA_CHANNEL);

	// creating mesh and adding mesh buffer
	SMesh* mesh = new SMesh();
	mesh->addMeshBuffer(meshBuffer);
	meshBuffer->drop();

	setMesh(mesh);
	mesh->drop();
}

CTexLineSceneNode::~CTexLineSceneNode()
{

}

void CTexLineSceneNode::setTextureBorders(f32 borderUp, f32 borderDown)
{
	for (int i = VerticesCount - 1; i >= 0; i--)
	{
		(*Vertices)[i].Color.set(0xFFFFFF);
		(*Vertices)[i].TCoords.X = i / 2;
		(*Vertices)[i].TCoords.Y = i % 2 ? borderUp : borderDown;
	}
}

void CTexLineSceneNode::setLineSegment(vector3df startPoint, vector3df endPoint)
{
	vector3df lineDirection = endPoint;
	lineDirection -= startPoint;

	// updating bounding box
	Box.reset(vector3df(0.0f, 0.0f, 0.0f));
	Box.addInternalPoint(lineDirection);
	Mesh->setBoundingBox(Box);

	setPosition(startPoint);
	updateAbsolutePosition();

	// correcting vertex mapping altered last time
	if (AlteredVertMap >= 0)
	{
		(*Vertices)[AlteredVertMap].TCoords.X = AlteredVertMap / 2;
		AlteredVertMap++;
		(*Vertices)[AlteredVertMap].TCoords.X = AlteredVertMap / 2;
		AlteredVertMap = -1;
	}

	// updating vertices positions
	f32 lineLength = lineDirection.getLength();
	lineDirection.normalize();

	vector3df lineDirPerp = Orientation.crossProduct(lineDirection);
	lineDirPerp.normalize(); // to check if needed
	lineDirPerp *= LineWidth;

	vector3df refPoint(0.0f, 0.0f, 0.0f);
	refPoint -= lineDirPerp * 0.5f;

	u16 fullSegments = lineLength / SegmStep;
	if (fullSegments >= MaxSegments)
	{
		lineDirection *= lineLength / MaxSegments;

		// positioning vertices to cover all line
		for (u16 i = 0; i < VerticesCount;)
		{
			(*Vertices)[i++].Pos = refPoint;
			(*Vertices)[i++].Pos = refPoint + lineDirPerp;
			refPoint += lineDirection;
		}

		// using all segments
		Indices->set_used(6 * MaxSegments);
	}
	else
	{
		lineDirection *= SegmStep;

		// positioning vertices to cover line with standard step
		u16 fullVertCnt = fullSegments * 2 + 2;
		u16 i;
		for (i = 0; i < fullVertCnt;)
		{
			(*Vertices)[i++].Pos = refPoint;
			(*Vertices)[i++].Pos = refPoint + lineDirPerp;
			refPoint += lineDirection;
		}

		AlteredVertMap = i; // will alterate mapping for the next 2 vertices

		// last 2 vertices will cover the rest of the line, we need to update their texture mapping
		f32 stepFrac = lineLength / SegmStep;
		stepFrac = stepFrac - floor(stepFrac);

		refPoint -= lineDirection;
		lineDirection.normalize();
		lineDirection *= SegmStep * stepFrac;
		refPoint += lineDirection;

		(*Vertices)[i].Pos = refPoint;
		(*Vertices)[i].TCoords.X = fullVertCnt / 2 - 1 + stepFrac;
		i++;

		(*Vertices)[i].Pos = refPoint + lineDirPerp;
		(*Vertices)[i].TCoords.X = fullVertCnt / 2 - 1 + stepFrac;

		// using a part of all segments
		Indices->set_used(6 * (fullSegments + 1));
	}
}

//void CTexLineSceneNode::render(void* renderData)
//{
//	CMeshSceneNode::render(renderData);
//}

void CTexLineSceneNode::OnRegisterSceneNode()
{
	if (IsVisible)
	{
		video::IVideoDriver* driver = SceneManager->getVideoDriver();

		PassCount = 0;

		if (Mesh)
		{
			for (u32 i = 0, cnt = Mesh->getMeshBufferCount(); i < cnt; ++i)
			{
				scene::IMeshBuffer* mb = Mesh->getMeshBuffer(i);
				if (mb)
				{
					mb->setIsVisible(true);
					const video::SMaterial& material = (ReadOnlyMaterials ? mb->getMaterial() : Materials[i]);

					video::IMaterialRenderer* rnd = driver->getMaterialRenderer(material.getMaterialType());

					// register according to material type
					SceneManager->registerNodeForRendering(this, &mb->getMaterial(), (void*)(i + 1), rnd->isTransparent()? scene::ESNRP_TRANSPARENT : scene::ESNRP_SOLID);
				}
			}
		}

		ISceneNode::OnRegisterSceneNode();
	}
}